package com.gs.netty.server;




import com.gs.netty.core.common.NettyConstants;
import com.gs.netty.core.factory.NettyThreadFactory;
import com.gs.netty.server.auth.WebSocketAuthHandler;
import com.gs.netty.support.dispatch.WebSocketDispatch;
import com.gs.netty.support.codec.WebSocketAcceptMessageProtocolDecoder;
import com.gs.netty.support.codec.WebSocketSendMessageProtocolEncoder;
import com.gs.netty.support.server.AbstractServer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;

import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;

import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PreDestroy;
import javax.net.ssl.SSLEngine;

@Slf4j
public class WebSocketServer extends AbstractServer {

    private final ServerBootstrap serverBootstrap;
    private final  EventLoopGroup bossGroup;
    private final  EventLoopGroup workerGroup;


    private final WebSocketAuthHandler  webSocketAuthHandler;

    private WebSocketServerDispatchHandler webSocketServerDispatchHandler;

    public WebSocketServer(NettyServerConfig nettyServerConfig, WebSocketDispatch webSocketDispatch,WebSocketAuthHandler webSocketAuthHandler) throws Exception {
        super(nettyServerConfig);
        this.serverBootstrap = new ServerBootstrap();
        this.webSocketAuthHandler = webSocketAuthHandler;
        bossGroup =  buildBossEventLoopGroupSelector();
        workerGroup = buildWorkerEventLoopGroupSelector();
        // 创建
        webSocketServerDispatchHandler = new WebSocketServerDispatchHandler(nettyServerConfig);
        webSocketServerDispatchHandler.setDispatch(webSocketDispatch);
    }

    @Override
    public void start() {
        NettyServerConfig nettyServerConfig = getBaseNettyConfig(NettyServerConfig.class);
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                // workerGroup 添加处理器
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        //获取到pipeline
                        ChannelPipeline pipeline = ch.pipeline();
                        if (isOpenSsl()) {
                            SSLEngine sslEngine = getSslContext().createSSLEngine();
                            sslEngine.setUseClientMode(nettyServerConfig.isUseClientMode());
                            sslEngine.setNeedClientAuth(nettyServerConfig.isNeedClientAuth());
                            SslHandler sslHandler = new SslHandler(sslEngine);
                            pipeline.addLast("sslHandler",sslHandler);
                            log.info("Add Prepend SSL handler");
                        }else {
                            log.warn("Connections are insecure as SSLContext is null!");
                        }

                        /**
                         * 使用 websocket 为什么要添加 HttpServerCodec，HttpObjectAggregator这两个处理器？
                         * 因为 websocket 建立连接时候会发一次 http 握手请求。握手成功了之后会从客户端的 SocketChannel移除这个两个处理器的
                         * HttpServerCodec 是用来解析 http 协议
                         *
                         * HttpObjectAggregator
                         */
                        pipeline.addLast("httpServerCodec",new HttpServerCodec());
                        pipeline.addLast("httpObjectAggregator",new HttpObjectAggregator(65536));
                        pipeline.addLast(new ChunkedWriteHandler());  // 处理大数据流


                        /**
                         * 添加服务权重处理器
                         */
                        pipeline.addLast(NettyConstants.AUTH_HANDLER,webSocketAuthHandler);
                        pipeline.addLast("webSocketServerProtocol",new WebSocketServerProtocolHandler(nettyServerConfig.getWebSocketPath(),
                                nettyServerConfig.getSubProtocols(),true));
                        pipeline.addLast(new WebSocketFrameAggregator(10 * 1024 * 1024));
                        pipeline.addLast(new WebSocketAcceptMessageProtocolDecoder());
                        pipeline.addLast(new WebSocketSendMessageProtocolEncoder());
                        pipeline.addLast("webSocketServerDispatchHandler",webSocketServerDispatchHandler);
                    }
                });

        try {
            serverBootstrap.bind(nettyServerConfig.getPort()).sync();
            log.info("webSocketServer started on port {}", nettyServerConfig.getPort());
        } catch (Exception e) {
            throw new IllegalStateException(String.format("webSocketServer Failed started to bind to %s:%d",
                    nettyServerConfig.getPort()), e);
        }
    }

    @Override
    public void init() {

    }



    @PreDestroy
    @Override
    public void shutdown() {
        if (closed.compareAndSet(false, true)) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}